package com.wgo.precise.server.web.servlet;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.List;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;

import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.HeaderFooter;
import com.lowagie.text.PageSize;
import com.lowagie.text.Phrase;
import com.lowagie.text.pdf.PdfWriter;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.wgo.bpot.server.persist.BasicPersistService;
import com.wgo.bpot.server.persist.PersistService;
import com.wgo.precise.server.pdf.EndOfPage;
import com.wgo.precise.server.pdf.PdfFactory;
import com.wgo.precise.server.pdf.PdfFactoryRegistry;

/**
 * a servlet that will generate a PDF document and send the document to the
 * client via the ServletOutputStream
 */
public class PdfServlet extends HttpServlet {

	private static final long serialVersionUID = 1L;

	private static final Logger log = Logger.getLogger(PdfServlet.class);

	private PdfFactoryRegistry pdfFactoryRegistry;

	private EndOfPage endOfPage;

	private PersistService persistService;

	/**
	 * Initialize the service, including the service object.
	 */
	@Override
	public void init(ServletConfig config) throws ServletException {
		super.init(config);
		try {
			String endOfPageText = getInitParameter("endOfPageText");
			endOfPage = new EndOfPage();
			endOfPage.setEndOfPageText(endOfPageText);
			String persistClassName = getInitParameter("persistence-class");
			if (null != persistClassName) {
				Class<BasicPersistService> persistenceClass = loadClass(persistClassName);
				persistService = persistenceClass.newInstance();
			}
			populateRegistry();
		} catch (Exception e) {
			log.error("Error while initializing servlet. " + e.getMessage(), e);
		}
	}
	
	private void populateRegistry(){
		pdfFactoryRegistry = new PdfFactoryRegistry();
		XStream xs = new XStream(new DomDriver());
		InputStream is = PdfServlet.class.getResourceAsStream("/pdf-factories.xml");
		if (null == is) {
			log.info("No PDF factories defined.  /pdf-factories.xml file does not exist in classpath.");
		}
		List<String> pdfFactoryClassNames = null;
		try {
			pdfFactoryClassNames = (List<String>)xs.fromXML(is);
			for (String factoryClassName : pdfFactoryClassNames) {
				pdfFactoryRegistry.addFactory((PdfFactory)loadClass(factoryClassName).newInstance());
			}
			pdfFactoryRegistry.initFactories();
		} catch (Exception e) {
			log.warn("Found /pdf-factories.xml file, but it has invalid format! I will consider this as if no triggers are defined.",e);
			return;
		}
	}

	@Override
	public void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws java.io.IOException {
		ByteArrayOutputStream baosPDF = null;
		try {
			pdfFactoryRegistry.setPersistService(persistService);
			baosPDF = generatePDFDocumentBytes(req, this.getServletContext());
			StringBuffer sbFilename = new StringBuffer();
			sbFilename.append("filename_");
			sbFilename.append(System.currentTimeMillis());
			sbFilename.append(".pdf");
			resp.setHeader("Cache-Control", "max-age=30");
			resp.setContentType("application/pdf");

			// The Content-disposition value will be in one of
			// two forms:
			// 1) inline; filename=foobar.pdf
			// 2) attachment; filename=foobar.pdf
			StringBuffer sbContentDispValue = new StringBuffer();
			sbContentDispValue.append("inline");
			sbContentDispValue.append("; filename=");
			sbContentDispValue.append(sbFilename);
			resp
					.setHeader("Content-disposition", sbContentDispValue
							.toString());
			resp.setContentLength(baosPDF.size());
			ServletOutputStream sos;
			sos = resp.getOutputStream();
			baosPDF.writeTo(sos);
			sos.flush();
		} catch (DocumentException dex) {
			resp.setContentType("text/html");
			PrintWriter writer = resp.getWriter();
			writer.println(this.getClass().getName() + " caught an exception: "
					+ dex.getClass().getName() + "<br>");
			writer.println("<pre>");
			dex.printStackTrace(writer);
			writer.println("</pre>");
		} finally {
			if (baosPDF != null) {
				baosPDF.reset();
			}
		}

	}

	/**
	 * 
	 * @param req
	 *            must be non-null
	 * 
	 * @return a non-null output stream. The output stream contains the bytes
	 *         for the PDF document
	 * 
	 * @throws DocumentException
	 * 
	 */
	protected ByteArrayOutputStream generatePDFDocumentBytes(
			final HttpServletRequest req, @SuppressWarnings("unused")
			final ServletContext ctx) throws DocumentException

	{
		Document doc = new Document();

		ByteArrayOutputStream baosPDF = new ByteArrayOutputStream();
		PdfWriter docWriter = null;

		try {
			docWriter = PdfWriter.getInstance(doc, baosPDF);
			docWriter.setPageEvent(endOfPage);
			doc.addAuthor(this.getClass().getName());
			doc.addCreationDate();
			doc.addProducer();
			doc.addCreator(this.getClass().getName());
			doc.addTitle("Precise Requirement");
			doc.setPageSize(PageSize.LETTER);
			HeaderFooter header = new HeaderFooter(new Phrase(
					"Precise Requirements"), false);
			doc.setHeader(header);
			doc.open();
			String compType = req.getParameter("component");
			String dbId = req.getParameter("dbId");
			pdfFactoryRegistry.writeDocument(doc, compType, dbId);
		} catch (DocumentException dex) {
			baosPDF.reset();
			throw dex;
		} finally {
			if (doc != null) {
				doc.close();
			}
			if (docWriter != null) {
				docWriter.close();
			}
		}
		if (baosPDF.size() < 1) {
			throw new DocumentException("document has " + baosPDF.size()
					+ " bytes");
		}
		return baosPDF;
	}

	private static Class loadClass(String className) throws ClassNotFoundException {
		ClassLoader loader = Thread.currentThread().getContextClassLoader();

		if (loader != null) {
			return Class.forName(className, false, loader);
		}
		return Class.forName(className);
	}

}
